home *** CD-ROM | disk | FTP | other *** search
- // 22/03/00 - Egg - Added MakeShadowMatrix (adapted from "OpenGL SuperBible" book)
- // 21/03/00 - Egg - Removed PWordArray (was a SysUtils's duplicate)
- // 06/02/00 - Egg - Added VectorEquals
- // 05/02/00 - Egg - Added some "const", more still needed
- // Added overloads for some of the MakeXXXVector funcs
- // Added homogeneous vector consts, VectorSpacing
- // I've added some perf enhancements, but basicly, dont use these funcs if speed
- // is an issue (especially the matrix stuff), they are still too memory intensive.
- // Prefer direct vector operations.
- //
- unit Geometry;
-
- // This unit contains many needed types, functions and procedures for
- // quaternion, vector and matrix arithmetics. It is specifically designed
- // for geometric calculations within R3 (affine vector space)
- // and R4 (homogeneous vector space).
- //
- // Note: The terms 'affine' or 'affine coordinates' are not really correct here
- // because an 'affine transformation' describes generally a transformation which leads
- // to a uniquely solvable system of equations and has nothing to do with the dimensionality
- // of a vector. One could use 'projective coordinates' but this is also not really correct
- // and since I haven't found a better name (or even any correct one), 'affine' is as good
- // as any other one.
- //
- // Identifiers containing no dimensionality (like affine or homogeneous)
- // and no datatype (integer..extended) are supposed as R4 representation
- // with 'single' floating point type (examples are TVector, TMatrix,
- // and TQuaternion). The default data type is 'single' ('GLFloat' for OpenGL)
- // and used in all routines (except conversions and trigonometric functions).
- //
- // Routines with an open array as argument can either take Func([1,2,3,4,..]) or Func(Vect).
- // The latter is prefered, since no extra stack operations is required.
- // Note: Be careful while passing open array elements! If you pass more elements
- // than there's room in the result the behaviour will be unpredictable.
- //
- // If not otherwise stated, all angles are given in radians
- // (instead of degrees). Use RadToDeg or DegToRad to convert between them.
- //
- // Geometry.pas was assembled from different sources (like GraphicGems)
- // and relevant books or based on self written code, respectivly.
- //
- // Note: Some aspects need to be considered when using Delphi and pure
- // assembler code. Delphi esnures that the direction flag is always
- // cleared while entering a function and expects it cleared on return.
- // This is in particular important in routines with (CPU) string commands (MOVSD etc.)
- // The registers EDI, ESI and EBX (as well as the stack management
- // registers EBP and ESP) must not be changed! EAX, ECX and EDX are
- // freely available and mostly used for parameter.
- //
- // Version 2.5
- // last change : 04. January 2000
- //
- // (c) Copyright 1999, Dipl. Ing. Mike Lischke (public@lischke-online.de)
-
- interface
-
- type
- // data types needed for 3D graphics calculation,
- // included are 'C like' aliases for each type (to be
- // conformal with OpenGL types)
-
- PByte = ^Byte;
- PWord = ^Word;
- PInteger = ^Integer;
- PFloat = ^Single;
- PDouble = ^Double;
- PExtended = ^Extended;
- PPointer = ^Pointer;
-
- // types to specify continous streams of a specific type
- // switch off range checking to access values beyond the limits
- PByteVector = ^TByteVector;
- PByteArray = PByteVector;
- TByteVector = array[0..0] of Byte;
-
- PWordVector = ^TWordVector;
- TWordVector = array[0..0] of Word;
-
- PIntegerVector = ^TIntegerVector;
- PIntegerArray = PIntegerVector;
- TIntegerVector = array[0..0] of Integer;
-
- PFloatVector = ^TFloatVector;
- PFloatArray = PFloatVector;
- TFloatVector = array[0..0] of Single;
-
- PDoubleVector = ^TDoubleVector;
- PDoubleArray = PDoubleVector;
- TDoubleVector = array[0..0] of Double;
-
- PExtendedVector = ^TExtendedVector;
- PExtendedArray = PExtendedVector;
- TExtendedVector = array[0..0] of Extended;
-
- PPointerVector = ^TPointerVector;
- PPointerArray = PPointerVector;
- TPointerVector = array[0..0] of Pointer;
-
- PCardinalVector = ^TCardinalVector;
- PCardinalArray = PCardinalVector;
- TCardinalVector = array[0..0] of Cardinal;
-
- // common vector and matrix types with predefined limits
- // indices correspond like: x -> 0
- // y -> 1
- // z -> 2
- // w -> 3
-
- PHomogeneousByteVector = ^THomogeneousByteVector;
- THomogeneousByteVector = array[0..3] of Byte;
- TVector4b = THomogeneousByteVector;
-
- PHomogeneousWordVector = ^THomogeneousWordVector;
- THomogeneousWordVector = array[0..3] of Word;
- TVector4w = THomogeneousWordVector;
-
- PHomogeneousIntVector = ^THomogeneousIntVector;
- THomogeneousIntVector = array[0..3] of Integer;
- TVector4i = THomogeneousIntVector;
-
- PHomogeneousFltVector = ^THomogeneousFltVector;
- THomogeneousFltVector = array[0..3] of Single;
- TVector4f = THomogeneousFltVector;
-
- PHomogeneousDblVector = ^THomogeneousDblVector;
- THomogeneousDblVector = array[0..3] of Double;
- TVector4d = THomogeneousDblVector;
-
- PHomogeneousExtVector = ^THomogeneousExtVector;
- THomogeneousExtVector = array[0..3] of Extended;
- TVector4e = THomogeneousExtVector;
-
- PHomogeneousPtrVector = ^THomogeneousPtrVector;
- THomogeneousPtrVector = array[0..3] of Pointer;
- TVector4p = THomogeneousPtrVector;
-
- PAffineByteVector = ^TAffineByteVector;
- TAffineByteVector = array[0..2] of Byte;
- TVector3b = TAffineByteVector;
-
- PAffineWordVector = ^TAffineWordVector;
- TAffineWordVector = array[0..2] of Word;
- TVector3w = TAffineWordVector;
-
- PAffineIntVector = ^TAffineIntVector;
- TAffineIntVector = array[0..2] of Integer;
- TVector3i = TAffineIntVector;
-
- PAffineFltVector = ^TAffineFltVector;
- TAffineFltVector = array[0..2] of Single;
- TVector3f = TAffineFltVector;
-
- PAffineDblVector = ^TAffineDblVector;
- TAffineDblVector = array[0..2] of Double;
- TVector3d = TAffineDblVector;
-
- PAffineExtVector = ^TAffineExtVector;
- TAffineExtVector = array[0..2] of Extended;
- TVector3e = TAffineExtVector;
-
- PAffinePtrVector = ^TAffinePtrVector;
- TAffinePtrVector = array[0..2] of Pointer;
- TVector3p = TAffinePtrVector;
-
- // some simplified names
- PVector = ^TVector;
- TVector = THomogeneousFltVector;
-
- PHomogeneousVector = ^THomogeneousVector;
- THomogeneousVector = THomogeneousFltVector;
-
- PAffineVector = ^TAffineVector;
- TAffineVector = TAffineFltVector;
-
- // arrays of vectors
- PVectorArray = ^TVectorArray;
- TVectorArray = array[0..0] of TAffineVector;
-
- // matrices
- THomogeneousByteMatrix = array[0..3] of THomogeneousByteVector;
- TMatrix4b = THomogeneousByteMatrix;
-
- THomogeneousWordMatrix = array[0..3] of THomogeneousWordVector;
- TMatrix4w = THomogeneousWordMatrix;
-
- THomogeneousIntMatrix = array[0..3] of THomogeneousIntVector;
- TMatrix4i = THomogeneousIntMatrix;
-
- THomogeneousFltMatrix = array[0..3] of THomogeneousFltVector;
- TMatrix4f = THomogeneousFltMatrix;
-
- THomogeneousDblMatrix = array[0..3] of THomogeneousDblVector;
- TMatrix4d = THomogeneousDblMatrix;
-
- THomogeneousExtMatrix = array[0..3] of THomogeneousExtVector;
- TMatrix4e = THomogeneousExtMatrix;
-
- TAffineByteMatrix = array[0..2] of TAffineByteVector;
- TMatrix3b = TAffineByteMatrix;
-
- TAffineWordMatrix = array[0..2] of TAffineWordVector;
- TMatrix3w = TAffineWordMatrix;
-
- TAffineIntMatrix = array[0..2] of TAffineIntVector;
- TMatrix3i = TAffineIntMatrix;
-
- TAffineFltMatrix = array[0..2] of TAffineFltVector;
- TMatrix3f = TAffineFltMatrix;
-
- TAffineDblMatrix = array[0..2] of TAffineDblVector;
- TMatrix3d = TAffineDblMatrix;
-
- TAffineExtMatrix = array[0..2] of TAffineExtVector;
- TMatrix3e = TAffineExtMatrix;
-
- // some simplified names
- PMatrix = ^TMatrix;
- TMatrix = THomogeneousFltMatrix;
-
- PHomogeneousMatrix = ^THomogeneousMatrix;
- THomogeneousMatrix = THomogeneousFltMatrix;
-
- PAffineMatrix = ^TAffineMatrix;
- TAffineMatrix = TAffineFltMatrix;
-
- // q = ([x, y, z], w)
- TQuaternion = record
- case Integer of
- 0:
- (ImagPart: TAffineVector;
- RealPart: Single);
- 1:
- (Vector: TVector4f);
- end;
-
- TRectangle = record
- Left,
- Top,
- Width,
- Height: Integer;
- end;
-
- TTransType = (ttScaleX, ttScaleY, ttScaleZ,
- ttShearXY, ttShearXZ, ttShearYZ,
- ttRotateX, ttRotateY, ttRotateZ,
- ttTranslateX, ttTranslateY, ttTranslateZ,
- ttPerspectiveX, ttPerspectiveY, ttPerspectiveZ, ttPerspectiveW);
-
- // used to describe a sequence of transformations in following order:
- // [Sx][Sy][Sz][ShearXY][ShearXZ][ShearZY][Rx][Ry][Rz][Tx][Ty][Tz][P(x,y,z,w)]
- // constants are declared for easier access (see MatrixDecompose below)
- TTransformations = array [TTransType] of Single;
-
-
- const
- // useful constants
-
- // standard vectors
- XVector : TAffineVector = (1, 0, 0);
- YVector : TAffineVector = (0, 1, 0);
- ZVector : TAffineVector = (0, 0, 1);
- XYZVector : TAffineVector = (1, 1, 1);
- NullVector : TAffineVector = (0, 0, 0);
- // standard homogeneous vectors
- XHmgVector : THomogeneousVector = (1, 0, 0, 0);
- YHmgVector : THomogeneousVector = (0, 1, 0, 0);
- ZHmgVector : THomogeneousVector = (0, 0, 1, 0);
- WHmgVector : THomogeneousVector = (0, 0, 0, 1);
- XYZHmgVector : THomogeneousVector = (1, 1, 1, 0);
- XYZWHmgVector : THomogeneousVector = (1, 1, 1, 1);
- NullHmgVector : THomogeneousVector = (0, 0, 0, 0);
- // standard homogeneous points
- XHmgPoint : THomogeneousVector = (1, 0, 0, 1);
- YHmgPoint : THomogeneousVector = (0, 1, 0, 1);
- ZHmgPoint : THomogeneousVector = (0, 0, 1, 1);
- WHmgPoint : THomogeneousVector = (0, 0, 0, 1);
- NullHmgPoint : THomogeneousVector = (0, 0, 0, 1);
-
- IdentityMatrix: TMatrix = ((1, 0, 0, 0),
- (0, 1, 0, 0),
- (0, 0, 1, 0),
- (0, 0, 0, 1));
- EmptyMatrix: TMatrix = ((0, 0, 0, 0),
- (0, 0, 0, 0),
- (0, 0, 0, 0),
- (0, 0, 0, 0));
- // some very small numbers
- EPSILON = 1e-100;
- EPSILON2 = 1e-50;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- // vector functions
- function VectorAdd(const V1, V2: TVector): TVector;
- //: Returns the sum of two vectors
- function VectorAffineAdd(const V1, V2: TAffineVector): TAffineVector;
- //: Makes a linear combination of two vectors and return the result
- function VectorAffineCombine(const V1, V2: TAffineVector; F1, F2: Single): TAffineVector;
- //: Makes a linear combination of three vectors and return the result
- function VectorAffineCombine3(const V1, V2, V3: TAffineVector; F1, F2, F3: Single): TAffineVector;
- function VectorAffineDotProduct(const V1, V2: TAffineVector): Single;
- function VectorAffineLerp(const V1, V2: TAffineVector; t: Single): TAffineVector;
- function VectorAffineSubtract(const V1, V2: TAffineVector): TAffineVector;
- function VectorAngle(const V1, V2: TAffineVector): Single;
- function VectorCombine(const V1, V2: TVector; F1, F2: Single): TVector;
- //: Calculates the cross product between vector 1 and 2
- function VectorCrossProduct(const V1, V2: TAffineVector): TAffineVector; overload;
- function VectorCrossProduct(const V1, V2: TVector): TVector; overload;
- function VectorDotProduct(const V1, V2: TVector): Single;
- function VectorLength(V: array of Single): Single;
- function VectorLerp(const V1, V2: TVector; t: Single): TVector;
- procedure VectorNegate(V: array of Single);
- //: Calculates norm of a vector which is defined as norm = x * x + y * y + ...
- function VectorNorm(V: array of Single): Single;
- //: Transforms a vector to unit length and return length
- function VectorNormalize(V: array of Single): Single;
- {: Calculates a vector perpendicular to N.<p>
- N is assumed to be of unit length, subtract out any component parallel to N }
- function VectorPerpendicular(const V, N: TAffineVector): TAffineVector;
- function VectorReflect(const V, N: TAffineVector): TAffineVector;
- //: Rotates Vector about Axis with Angle radiants
- procedure VectorRotate(var Vector: TVector4f; const Axis: TVector3f; Angle: Single);
- //: Returns a vector scaled by a factor
- procedure VectorScale(V: array of Single; Factor: Single);
- //: Returns V1-V2
- function VectorSubtract(const V1, V2: TVector): TVector;
- {: Calculates Abs(v1[x]-v2[x])+Abs(v1[y]-v2[y])+ etc.<p> }
- function VectorSpacing(const V1, V2: TVector): Single;
- //: True if all components are equal.
- function VectorEquals(const V1, V2: TVector) : Boolean; overload;
- //: True if all components are equal.
- function VectorEquals(const V1, V2: TAffineVector) : Boolean; overload;
-
- // matrix functions
- function CreateRotationMatrixX(Sine, Cosine: Single): TMatrix;
- function CreateRotationMatrixY(Sine, Cosine: Single): TMatrix;
- function CreateRotationMatrixZ(Sine, Cosine: Single): TMatrix;
- function CreateScaleMatrix(const V: TAffineVector): TMatrix;
- function CreateTranslationMatrix(const V: TVector): TMatrix;
- procedure MatrixAdjoint(var M: TMatrix);
- function MatrixAffineDeterminant(const M: TAffineMatrix): Single;
- procedure MatrixAffineTranspose(var M: TAffineMatrix);
- function MatrixDeterminant(const M: TMatrix): Single;
- procedure MatrixInvert(var M: TMatrix);
- function MatrixMultiply(const M1, M2: TMatrix): TMatrix;
- procedure MatrixScale(var M: TMatrix; Factor: Single);
- procedure MatrixTranspose(var M: TMatrix);
-
- // quaternion functions
- function QuaternionConjugate(const Q: TQuaternion): TQuaternion;
- function QuaternionFromPoints(const V1, V2: TAffineVector): TQuaternion;
- function QuaternionMultiply(const qL, qR: TQuaternion): TQuaternion;
- function QuaternionSlerp(const QStart, QEnd: TQuaternion; Spin: Integer; t: Single): TQuaternion;
- function QuaternionToMatrix(const Q: TQuaternion): TMatrix;
- procedure QuaternionToPoints(const Q: TQuaternion; var ArcFrom, ArcTo: TAffineVector);
-
- // mixed functions
- function ConvertRotation(const Angles: TAffineVector): TVector;
- function CreateRotationMatrix(const Axis: TVector3f; Angle: Single): TMatrix;
- function MatrixDecompose(const M: TMatrix; var Tran: TTransformations): Boolean;
- //: Transforms an affine vector by multiplying it with a matrix
- function VectorAffineTransform(const V: TAffineVector; const M: TAffineMatrix): TAffineVector;
- //: Transforms a homogeneous vector by multiplying it with a matrix
- function VectorTransform(const V: TVector4f; const M: TMatrix): TVector4f; overload;
- //: Transforms an affine vector by multiplying it with a (homogeneous) matrix
- function VectorTransform(const V: TVector3f; const M: TMatrix): TVector3f; overload;
-
- // miscellaneous functions
- function MakeAffineDblVector(V: array of Double): TAffineDblVector;
- function MakeDblVector(V: array of Double): THomogeneousDblVector;
- function MakeAffineVector(V: array of Single) : TAffineVector; overload
- function MakeAffineVector(const x, y, z : Single) : TAffineVector; overload;
- function MakeQuaternion(Imag: array of Single; Real: Single): TQuaternion;
- function MakeVector(V: array of Single) : TVector; overload;
- function MakeVector(const x, y, z: Single; w : Single = 0) : TVector; overload;
- function MakePoint(const x, y, z: Single; w : Single = 1) : TVector; overload;
- function PointInPolygon(xp, yp : array of Single; x, y: Single): Boolean;
- function VectorAffineDblToFlt(const V: TAffineDblVector): TAffineVector;
- function VectorDblToFlt(const V: THomogeneousDblVector): THomogeneousVector;
- function VectorAffineFltToDbl(const V: TAffineVector): TAffineDblVector;
- function VectorFltToDbl(const V: TVector): THomogeneousDblVector;
-
- // trigonometric functions
- function ArcCos(const X: Extended): Extended;
- function ArcSin(const X: Extended): Extended;
- function ArcTan2(const Y, X: Extended): Extended;
- function CoTan(const X: Extended): Extended;
- function DegToRad(const Degrees: Extended): Extended;
- function RadToDeg(const Radians: Extended): Extended;
- //: Calculates sine and cosine from the given angle Theta
- procedure SinCos(const Theta: Extended; var Sin, Cos: Extended);
- function Tan(const X: Extended): Extended;
-
- // coordinate system manipulation functions
-
- //: Rotates the given coordinate system (represented by the matrix) around its Y-axis
- function Turn(const Matrix: TMatrix; Angle: Single): TMatrix; overload;
- //: Rotates the given coordinate system (represented by the matrix) around MasterUp
- function Turn(const Matrix: TMatrix; const MasterUp: TAffineVector; Angle: Single): TMatrix; overload;
- //: Rotates the given coordinate system (represented by the matrix) around its X-axis
- function Pitch(const Matrix: TMatrix; Angle: Single): TMatrix; overload;
- //: Rotates the given coordinate system (represented by the matrix) around MasterRight
- function Pitch(const Matrix: TMatrix; const MasterRight: TAffineVector; Angle: Single): TMatrix; overload;
- //: Rotates the given coordinate system (represented by the matrix) around its Z-axis
- function Roll(const Matrix: TMatrix; Angle: Single): TMatrix; overload;
- //: Rotates the given coordinate system (represented by the matrix) around MasterDirection
- function Roll(const Matrix: TMatrix; const MasterDirection: TAffineVector; Angle: Single): TMatrix; overload;
-
- // misc funcs
-
- // Creates a shadow projection matrix out of the plane equation
- // coefficients and the position of the light. The return value is stored
- // in destMat[][]
- function MakeShadowMatrix(const planePoint, planeNormal, lightPos : TVector) : TMatrix;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- implementation
-
- const
- // FPU status flags (high order byte)
- C0 = 1;
- C1 = 2;
- C2 = 4;
- C3 = $40;
-
- // to be used as descriptive indices
- X = 0;
- Y = 1;
- Z = 2;
- W = 3;
-
- //----------------- trigonometric helper functions ---------------------------------------------------------------------
-
- function DegToRad(const Degrees: Extended): Extended;
-
- begin
- Result := Degrees * (PI / 180);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function RadToDeg(const Radians: Extended): Extended;
-
- begin
- Result := Radians * (180 / PI);
- end;
-
- // SinCos (Extended)
- //
- procedure SinCos(const Theta: Extended; var Sin, Cos: Extended); assembler; register;
- // EAX contains address of Sin
- // EDX contains address of Cos
- // Theta is passed over the stack
- asm
- FLD Theta
- FSINCOS
- FSTP TBYTE PTR [EDX] // cosine
- FSTP TBYTE PTR [EAX] // sine
- FWAIT
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function ArcCos(const X: Extended): Extended;
-
- begin
- Result := ArcTan2(Sqrt(1 - X * X), X);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function ArcSin(const X: Extended): Extended;
-
- begin
- Result := ArcTan2(X, Sqrt(1 - X * X))
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function ArcTan2(const Y, X: Extended): Extended;
-
- asm
- FLD Y
- FLD X
- FPATAN
- FWAIT
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function Tan(const X: Extended): Extended;
-
- asm
- FLD X
- FPTAN
- FSTP ST(0) // FPTAN pushes 1.0 after result
- FWAIT
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function CoTan(const X: Extended): Extended;
-
- asm
- FLD X
- FPTAN
- FDIVRP
- FWAIT
- end;
-
- //----------------- miscellaneous vector functions ---------------------------------------------------------------------
-
- function MakeAffineDblVector(V: array of Double): TAffineDblVector; assembler;
-
- // creates a vector from given values
- // EAX contains address of V
- // ECX contains address to result vector
- // EDX contains highest index of V
-
- asm
- PUSH EDI
- PUSH ESI
- MOV EDI, ECX
- MOV ESI, EAX
- MOV ECX, EDX
- ADD ECX, 2
- REP MOVSD
- POP ESI
- POP EDI
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MakeDblVector(V: array of Double): THomogeneousDblVector; assembler;
-
- // creates a vector from given values
- // EAX contains address of V
- // ECX contains address to result vector
- // EDX contains highest index of V
-
- asm
- PUSH EDI
- PUSH ESI
- MOV EDI, ECX
- MOV ESI, EAX
- MOV ECX, EDX
- ADD ECX, 2
- REP MOVSD
- POP ESI
- POP EDI
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MakeAffineVector(V: array of Single): TAffineVector; assembler;
- // creates a vector from given values
- // EAX contains address of V
- // ECX contains address to result vector
- // EDX contains highest index of V
- asm
- PUSH EDI
- PUSH ESI
- MOV EDI, ECX
- MOV ESI, EAX
- MOV ECX, EDX
- INC ECX
- CMP ECX, 3
- JB @@1
- MOV ECX, 3
- @@1: REP MOVSD // copy given values
- MOV ECX, 2
- SUB ECX, EDX // determine missing entries
- JS @@Finish
- XOR EAX, EAX
- REP STOSD // set remaining fields to 0
- @@Finish: POP ESI
- POP EDI
- end;
-
- // MakeAffineVector
- //
- function MakeAffineVector(const x, y, z : Single) : TAffineVector; overload;
- begin
- Result[0]:=x;
- Result[1]:=y;
- Result[2]:=z;
- end;
-
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MakeQuaternion(Imag: array of Single; Real: Single): TQuaternion; assembler;
-
- // creates a quaternion from the given values
- // EAX contains address of Imag
- // ECX contains address to result vector
- // EDX contains highest index of Imag
- // Real part is passed on the stack
-
- asm
- PUSH EDI
- PUSH ESI
- MOV EDI, ECX
- MOV ESI, EAX
- MOV ECX, EDX
- INC ECX
- REP MOVSD
- MOV EAX, [Real]
- MOV [EDI], EAX
- POP ESI
- POP EDI
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MakeVector(V: array of Single): TVector; assembler;
- // creates a vector from given values
- // EAX contains address of V
- // ECX contains address to result vector
- // EDX contains highest index of V
- asm
- PUSH EDI
- PUSH ESI
- MOV EDI, ECX
- MOV ESI, EAX
- MOV ECX, EDX
- INC ECX
- CMP ECX, 4
- JB @@1
- MOV ECX, 4
- @@1: REP MOVSD // copy given values
- MOV ECX, 3
- SUB ECX, EDX // determine missing entries
- JS @@Finish
- XOR EAX, EAX
- REP STOSD // set remaining fields to 0
- @@Finish: POP ESI
- POP EDI
- end;
-
- // MakeVector
- //
- function MakeVector(const x, y, z : Single; w : Single = 0) : TVector;
- begin
- Result[0]:=x;
- Result[1]:=y;
- Result[2]:=z;
- Result[3]:=w;
- end;
-
- // MakePoint
- //
- function MakePoint(const x, y, z: Single; w : Single = 1) : TVector;
- begin
- Result[0]:=x;
- Result[1]:=y;
- Result[2]:=z;
- Result[3]:=w;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorLength(V: array of Single): Single; assembler;
-
- // calculates the length of a vector following the equation: sqrt(x * x + y * y + ...)
- // Note: The parameter of this function is declared as open array. Thus
- // there's no restriction about the number of the components of the vector.
- //
- // EAX contains address of V
- // EDX contains the highest index of V
- // the result is returned in ST(0)
-
- asm
- FLDZ // initialize sum
- @@Loop: FLD DWORD PTR [EAX + 4 * EDX] // load a component
- FMUL ST, ST
- FADDP
- SUB EDX, 1
- JNL @@Loop
- FSQRT
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAngle(const V1, V2: TAffineVector): Single; assembler;
-
- // calculates the cosine of the angle between Vector1 and Vector2
- // Result = DotProduct(V1, V2) / (Length(V1) * Length(V2))
- //
- // EAX contains address of Vector1
- // EDX contains address of Vector2
-
- asm
- FLD DWORD PTR [EAX] // V1[0]
- FLD ST // double V1[0]
- FMUL ST, ST // V1[0]^2 (prep. for divisor)
- FLD DWORD PTR [EDX] // V2[0]
- FMUL ST(2), ST // ST(2) := V1[0] * V2[0]
- FMUL ST, ST // V2[0]^2 (prep. for divisor)
- FLD DWORD PTR [EAX + 4] // V1[1]
- FLD ST // double V1[1]
- FMUL ST, ST // ST(0) := V1[1]^2
- FADDP ST(3), ST // ST(2) := V1[0]^2 + V1[1] * * 2
- FLD DWORD PTR [EDX + 4] // V2[1]
- FMUL ST(1), ST // ST(1) := V1[1] * V2[1]
- FMUL ST, ST // ST(0) := V2[1]^2
- FADDP ST(2), ST // ST(1) := V2[0]^2 + V2[1]^2
- FADDP ST(3), ST // ST(2) := V1[0] * V2[0] + V1[1] * V2[1]
- FLD DWORD PTR [EAX + 8] // load V2[1]
- FLD ST // same calcs go here
- FMUL ST, ST // (compare above)
- FADDP ST(3), ST
- FLD DWORD PTR [EDX + 8]
- FMUL ST(1), ST
- FMUL ST, ST
- FADDP ST(2), ST
- FADDP ST(3), ST
- FMULP // ST(0) := (V1[0]^2 + V1[1]^2 + V1[2]) *
- // (V2[0]^2 + V2[1]^2 + V2[2])
- FSQRT // sqrt(ST(0))
- FDIVP // ST(0) := Result := ST(1) / ST(0)
- // the result is expected in ST(0), if it's invalid, an error is raised
- end;
-
- // VectorNorm
- //
- function VectorNorm(V: array of Single): Single; assembler; register;
- // EAX contains address of V
- // EDX contains highest index in V
- // result is passed in ST(0)
- asm
- FLDZ // initialize sum
- @@Loop: FLD DWORD PTR [EAX + 4 * EDX] // load a component
- FMUL ST, ST // make square
- FADDP // add previous calculated sum
- SUB EDX, 1
- JNL @@Loop
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorNormalize(V: array of Single): Single; assembler; register;
-
- // EAX contains address of V
- // EDX contains the highest index in V
- // return former length of V in ST
-
- asm
- PUSH EBX
- MOV ECX, EDX // save size of V
- CALL VectorLength // calculate length of vector
- FTST // test if length = 0
- MOV EBX, EAX // save parameter address
- FSTSW AX // get test result
- TEST AH, C3 // check the test result
- JNZ @@Finish
- SUB EBX, 4 // simplyfied address calculation
- INC ECX
- FLD1 // calculate reciprocal of length
- FDIV ST, ST(1)
- @@1: FLD ST // double reciprocal
- FMUL DWORD PTR [EBX + 4 * ECX] // scale component
- WAIT
- FSTP DWORD PTR [EBX + 4 * ECX] // store result
- LOOP @@1
- FSTP ST // remove reciprocal from FPU stack
- @@Finish: POP EBX
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAffineSubtract(const V1, V2: TAffineVector): TAffineVector; assembler; register;
-
- // returns v1 minus v2
- // EAX contains address of V1
- // EDX contains address of V2
- // ECX contains address of the result
-
- asm
- {Result[X] := V1[X]-V2[X];
- Result[Y] := V1[Y]-V2[Y];
- Result[Z] := V1[Z]-V2[Z];}
-
- FLD DWORD PTR [EAX]
- FSUB DWORD PTR [EDX]
- FSTP DWORD PTR [ECX]
- FLD DWORD PTR [EAX + 4]
- FSUB DWORD PTR [EDX + 4]
- FSTP DWORD PTR [ECX + 4]
- FLD DWORD PTR [EAX + 8]
- FSUB DWORD PTR [EDX + 8]
- FSTP DWORD PTR [ECX + 8]
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorReflect(const V, N: TAffineVector): TAffineVector; assembler; register;
-
- // reflects vector V against N (assumes N is normalized)
- // EAX contains address of V
- // EDX contains address of N
- // ECX contains address of the result
-
- //var Dot : Single;
-
- asm
- {Dot := VectorAffineDotProduct(V, N);
- Result[X] := V[X]-2 * Dot * N[X];
- Result[Y] := V[Y]-2 * Dot * N[Y];
- Result[Z] := V[Z]-2 * Dot * N[Z];}
-
- CALL VectorAffineDotProduct // dot is now in ST(0)
- FCHS // -dot
- FADD ST, ST // -dot * 2
- FLD DWORD PTR [EDX] // ST := N[X]
- FMUL ST, ST(1) // ST := -2 * dot * N[X]
- FADD DWORD PTR[EAX] // ST := V[X] - 2 * dot * N[X]
- FSTP DWORD PTR [ECX] // store result
- FLD DWORD PTR [EDX + 4] // etc.
- FMUL ST, ST(1)
- FADD DWORD PTR[EAX + 4]
- FSTP DWORD PTR [ECX + 4]
- FLD DWORD PTR [EDX + 8]
- FMUL ST, ST(1)
- FADD DWORD PTR[EAX + 8]
- FSTP DWORD PTR [ECX + 8]
- FSTP ST // clean FPU stack
- end;
-
- // VectorRotate
- //
- procedure VectorRotate(var Vector: TVector4f; const Axis: TVector3f; Angle: Single);
- var
- rotMatrix : TMatrix4f;
- begin
- rotMatrix:=CreateRotationMatrix(Axis, Angle);
- vector:=VectorTransform(Vector, RotMatrix);
- end;
-
- // VectorScale
- //
- procedure VectorScale(V: array of Single; Factor: Single); assembler; register;
-
- // EAX contains address of V
- // EDX contains highest index in V
- // Factor is located on the stack
-
- asm
- {for I := Low(V) to High(V) do V[I] := V[I] * Factor;}
-
- FLD DWORD PTR [Factor] // load factor
- @@Loop: FLD DWORD PTR [EAX + 4 * EDX] // load a component
- FMUL ST, ST(1) // multiply it with the factor
- WAIT
- FSTP DWORD PTR [EAX + 4 * EDX] // store the result
- DEC EDX // do the entire array
- JNS @@Loop
- FSTP ST(0) // clean the FPU stack
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure VectorNegate(V: array of Single); assembler; register;
-
- // returns a negated vector
- // EAX contains address of V
- // EDX contains highest index in V
-
- asm
- {V[X] := -V[X];
- V[Y] := -V[Y];
- V[Z] := -V[Z];}
-
- @@Loop: FLD DWORD PTR [EAX + 4 * EDX]
- FCHS
- WAIT
- FSTP DWORD PTR [EAX + 4 * EDX]
- DEC EDX
- JNS @@Loop
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAdd(const V1, V2: TVector): TVector; register;
- begin
- Result[X] := V1[X] + V2[X];
- Result[Y] := V1[Y] + V2[Y];
- Result[Z] := V1[Z] + V2[Z];
- Result[W] := V1[W] + V2[W];
- end;
-
- // VectorAffineAdd
- //
- function VectorAffineAdd(const V1, V2: TAffineVector): TAffineVector; register;
- begin
- Result[X] := V1[X] + V2[X];
- Result[Y] := V1[Y] + V2[Y];
- Result[Z] := V1[Z] + V2[Z];
- end;
-
- // VectorSubtract
- //
- function VectorSubtract(const V1, V2: TVector): TVector; register;
- begin
- Result[X] := V1[X] - V2[X];
- Result[Y] := V1[Y] - V2[Y];
- Result[Z] := V1[Z] - V2[Z];
- Result[W] := V1[W] - V2[W];
- end;
-
- // VectorSpacing
- //
- function VectorSpacing(const V1, V2: TVector): Single;
- begin
- Result:=Abs(V1[X]-V2[X])+Abs(V1[Y]-V2[Y])+Abs(V1[Z]-V2[Z])+Abs(V1[W]-V2[W]);
- end;
-
- // VectorEquals (hmg vector)
- //
- function VectorEquals(const V1, V2: TVector) : Boolean;
- begin
- Result:=((V1[0]=V2[0]) and (V1[1]=V2[1]) and (V1[2]=V2[2]) and (V1[3]=V2[3]));
- end;
-
- // VectorEquals (affine vector)
- //
- function VectorEquals(const V1, V2: TAffineVector) : Boolean;
- begin
- Result:=((V1[0]=V2[0]) and (V1[1]=V2[1]) and (V1[2]=V2[2]));
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorDotProduct(const V1, V2: TVector): Single; register;
-
- begin
- Result := V1[X] * V2[X] + V1[Y] * V2[Y] + V1[Z] * V2[Z] + V1[W] * V2[W];
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAffineDotProduct(const V1, V2: TAffineVector): Single; assembler; register;
-
- // calculates the dot product between V1 and V2
- // EAX contains address of V1
- // EDX contains address of V2
- // result is stored in ST(0)
-
- asm
- //Result := V1[X] * V2[X] + V1[Y] * V2[Y] + V1[Z] * V2[Z];
-
- FLD DWORD PTR [EAX]
- FMUL DWORD PTR [EDX]
- FLD DWORD PTR [EAX + 4]
- FMUL DWORD PTR [EDX + 4]
- FADDP
- FLD DWORD PTR [EAX + 8]
- FMUL DWORD PTR [EDX + 8]
- FADDP
- end;
-
- // VectorCrossProduct
- //
- function VectorCrossProduct(const V1, V2: TAffineVector): TAffineVector;
-
- // Temp is necessary because
- // either V1 or V2 could also be the result vector
- //
- // EAX contains address of V1
- // EDX contains address of V2
- // ECX contains address of result
-
- var Temp: TAffineVector;
-
- asm
- {Temp[X] := V1[Y] * V2[Z]-V1[Z] * V2[Y];
- Temp[Y] := V1[Z] * V2[X]-V1[X] * V2[Z];
- Temp[Z] := V1[X] * V2[Y]-V1[Y] * V2[X];
- Result := Temp;}
-
- PUSH EBX // save EBX, must be restored to original value
- LEA EBX, [Temp]
- FLD DWORD PTR [EDX + 8] // first load both vectors onto FPU register stack
- FLD DWORD PTR [EDX + 4]
- FLD DWORD PTR [EDX + 0]
- FLD DWORD PTR [EAX + 8]
- FLD DWORD PTR [EAX + 4]
- FLD DWORD PTR [EAX + 0]
-
- FLD ST(1) // ST(0) := V1[Y]
- FMUL ST, ST(6) // ST(0) := V1[Y] * V2[Z]
- FLD ST(3) // ST(0) := V1[Z]
- FMUL ST, ST(6) // ST(0) := V1[Z] * V2[Y]
- FSUBP ST(1), ST // ST(0) := ST(1)-ST(0)
- FSTP DWORD [EBX] // Temp[X] := ST(0)
- FLD ST(2) // ST(0) := V1[Z]
- FMUL ST, ST(4) // ST(0) := V1[Z] * V2[X]
- FLD ST(1) // ST(0) := V1[X]
- FMUL ST, ST(7) // ST(0) := V1[X] * V2[Z]
- FSUBP ST(1), ST // ST(0) := ST(1)-ST(0)
- FSTP DWORD [EBX + 4] // Temp[Y] := ST(0)
- FLD ST // ST(0) := V1[X]
- FMUL ST, ST(5) // ST(0) := V1[X] * V2[Y]
- FLD ST(2) // ST(0) := V1[Y]
- FMUL ST, ST(5) // ST(0) := V1[Y] * V2[X]
- FSUBP ST(1), ST // ST(0) := ST(1)-ST(0)
- FSTP DWORD [EBX + 8] // Temp[Z] := ST(0)
- FSTP ST(0) // clear FPU register stack
- FSTP ST(0)
- FSTP ST(0)
- FSTP ST(0)
- FSTP ST(0)
- FSTP ST(0)
- MOV EAX, [EBX] // copy Temp to Result
- MOV [ECX], EAX
- MOV EAX, [EBX + 4]
- MOV [ECX + 4], EAX
- MOV EAX, [EBX + 8]
- MOV [ECX + 8], EAX
- POP EBX
- end;
-
- // VectorCrossProduct
- //
- function VectorCrossProduct(const V1, V2: TVector): TVector;
- begin
- Result[X] := V1[Y] * V2[Z]-V1[Z] * V2[Y];
- Result[Y] := V1[Z] * V2[X]-V1[X] * V2[Z];
- Result[Z] := V1[X] * V2[Y]-V1[Y] * V2[X];
- Result[W] := 0;
- end;
-
- // TAffineVector
- //
- function VectorPerpendicular(const V, N: TAffineVector): TAffineVector;
- var
- dot : Single;
- begin
- dot := VectorAffineDotProduct(V, N);
- Result[X] := V[X]-Dot * N[X];
- Result[Y] := V[Y]-Dot * N[Y];
- Result[Z] := V[Z]-Dot * N[Z];
- end;
-
- // VectorTransform
- //
- function VectorTransform(const V: TVector4f; const M: TMatrix): TVector4f; register;
- var
- TV: TVector4f;
- begin
- TV[X] := V[X] * M[X, X] + V[Y] * M[Y, X] + V[Z] * M[Z, X] + V[W] * M[W, X];
- TV[Y] := V[X] * M[X, Y] + V[Y] * M[Y, Y] + V[Z] * M[Z, Y] + V[W] * M[W, Y];
- TV[Z] := V[X] * M[X, Z] + V[Y] * M[Y, Z] + V[Z] * M[Z, Z] + V[W] * M[W, Z];
- TV[W] := V[X] * M[X, W] + V[Y] * M[Y, W] + V[Z] * M[Z, W] + V[W] * M[W, W];
- Result := TV
- end;
-
- // VectorTransform
- //
- function VectorTransform(const V: TVector3f; const M: TMatrix): TVector3f;
- var
- TV: TVector3f;
- begin
- TV[X] := V[X] * M[X, X] + V[Y] * M[Y, X] + V[Z] * M[Z, X] + M[W, X];
- TV[Y] := V[X] * M[X, Y] + V[Y] * M[Y, Y] + V[Z] * M[Z, Y] + M[W, Y];
- TV[Z] := V[X] * M[X, Z] + V[Y] * M[Y, Z] + V[Z] * M[Z, Z] + M[W, Z];
- Result := TV;
- end;
-
- // VectorAffineTransform
- //
- function VectorAffineTransform(const V: TAffineVector; const M: TAffineMatrix): TAffineVector; register;
- var
- TV: TAffineVector;
- begin
- TV[X] := V[X] * M[X, X] + V[Y] * M[Y, X] + V[Z] * M[Z, X];
- TV[Y] := V[X] * M[X, Y] + V[Y] * M[Y, Y] + V[Z] * M[Z, Y];
- TV[Z] := V[X] * M[X, Z] + V[Y] * M[Y, Z] + V[Z] * M[Z, Z];
- Result := TV;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function PointInPolygon(xp, yp : array of Single; x, y: Single) : Boolean;
- // The code below is from Wm. Randolph Franklin <wrf@ecse.rpi.edu>
- // with some minor modifications for speed. It returns 1 for strictly
- // interior points, 0 for strictly exterior, and 0 or 1 for points on
- // the boundary.
- // This code is not yet tested!
- var
- i, j: Integer;
- begin
- Result:=False;
- if High(XP)=High(YP) then begin
- j:=High(XP);
- for i:=0 to High(XP) do begin
- if ( ( ((yp[I]<=y) and (y<yp[J])) or ((yp[J]<=y) and (y<yp[I])) )
- and (x<(xp[J]-xp[I])*(y-yp[I])/(yp[J]-yp[I])+xp[I])) then
- Result := not Result;
- j:=i;
- //jh mod
- end;
- end;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function QuaternionConjugate(const Q: TQuaternion): TQuaternion; assembler;
-
- // returns the conjugate of a quaternion
- // EAX contains address of Q
- // EDX contains address of result
-
- asm
- FLD DWORD PTR [EAX]
- FCHS
- WAIT
- FSTP DWORD PTR [EDX]
- FLD DWORD PTR [EAX + 4]
- FCHS
- WAIT
- FSTP DWORD PTR [EDX + 4]
- FLD DWORD PTR [EAX + 8]
- FCHS
- WAIT
- FSTP DWORD PTR [EDX + 8]
- MOV EAX, [EAX + 12]
- MOV [EDX + 12], EAX
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function QuaternionFromPoints(const V1, V2: TAffineVector): TQuaternion; assembler;
-
- // constructs a unit quaternion from two points on unit sphere
- // EAX contains address of V1
- // ECX contains address to result
- // EDX contains address of V2
-
- asm
- {Result.ImagPart := VectorCrossProduct(V1, V2);
- Result.RealPart := Sqrt((VectorAffineDotProduct(V1, V2) + 1)/2);}
-
- PUSH EAX
- CALL VectorCrossProduct // determine axis to rotate about
- POP EAX
- FLD1 // prepare next calculation
- Call VectorAffineDotProduct // calculate cos(angle between V1 and V2)
- FADD ST, ST(1) // transform angle to angle/2 by: cos(a/2)=sqrt((1 + cos(a))/2)
- FXCH ST(1)
- FADD ST, ST
- FDIVP ST(1), ST
- FSQRT
- FSTP DWORD PTR [ECX + 12] // Result.RealPart := ST(0)
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function QuaternionMultiply(const qL, qR: TQuaternion): TQuaternion;
-
- // Returns quaternion product qL * qR. Note: order is important!
- // To combine rotations, use the product QuaternionMuliply(qSecond, qFirst),
- // which gives the effect of rotating by qFirst then qSecond.
-
- var Temp : TQuaternion;
-
- begin
- Temp.RealPart := qL.RealPart * qR.RealPart - qL.ImagPart[X] * qR.ImagPart[X] -
- qL.ImagPart[Y] * qR.ImagPart[Y] - qL.ImagPart[Z] * qR.ImagPart[Z];
- Temp.ImagPart[X] := qL.RealPart * qR.ImagPart[X] + qL.ImagPart[X] * qR.RealPart +
- qL.ImagPart[Y] * qR.ImagPart[Z] - qL.ImagPart[Z] * qR.ImagPart[Y];
- Temp.ImagPart[Y] := qL.RealPart * qR.ImagPart[Y] + qL.ImagPart[Y] * qR.RealPart +
- qL.ImagPart[Z] * qR.ImagPart[X] - qL.ImagPart[X] * qR.ImagPart[Z];
- Temp.ImagPart[Z] := qL.RealPart * qR.ImagPart[Z] + qL.ImagPart[Z] * qR.RealPart +
- qL.ImagPart[X] * qR.ImagPart[Y] - qL.ImagPart[Y] * qR.ImagPart[X];
- Result := Temp;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function QuaternionToMatrix(const Q: TQuaternion): TMatrix;
-
- // Constructs rotation matrix from (possibly non-unit) quaternion.
- // Assumes matrix is used to multiply column vector on the left:
- // vnew = mat vold. Works correctly for right-handed coordinate system
- // and right-handed rotations.
-
- // Essentially, this function is the same as CreateRotationMatrix and you can consider it as
- // being for reference here.
-
- {var Norm, S,
- XS, YS, ZS,
- WX, WY, WZ,
- XX, XY, XZ,
- YY, YZ, ZZ : Single;
-
- begin
- Norm := Q.Vector[X] * Q.Vector[X] + Q.Vector[Y] * Q.Vector[Y] + Q.Vector[Z] * Q.Vector[Z] + Q.RealPart * Q.RealPart;
- if Norm > 0 then S := 2 / Norm
- else S := 0;
-
- XS := Q.Vector[X] * S; YS := Q.Vector[Y] * S; ZS := Q.Vector[Z] * S;
- WX := Q.RealPart * XS; WY := Q.RealPart * YS; WZ := Q.RealPart * ZS;
- XX := Q.Vector[X] * XS; XY := Q.Vector[X] * YS; XZ := Q.Vector[X] * ZS;
- YY := Q.Vector[Y] * YS; YZ := Q.Vector[Y] * ZS; ZZ := Q.Vector[Z] * ZS;
-
- Result[X, X] := 1 - (YY + ZZ); Result[Y, X] := XY + WZ; Result[Z, X] := XZ - WY; Result[W, X] := 0;
- Result[X, Y] := XY - WZ; Result[Y, Y] := 1 - (XX + ZZ); Result[Z, Y] := YZ + WX; Result[W, Y] := 0;
- Result[X, Z] := XZ + WY; Result[Y, Z] := YZ - WX; Result[Z, Z] := 1 - (XX + YY); Result[W, Z] := 0;
- Result[X, W] := 0; Result[Y, W] := 0; Result[Z, W] := 0; Result[W, W] := 1;}
-
- var
- V: TAffineVector;
- SinA, CosA,
- A, B, C: Extended;
-
- begin
- V := Q.ImagPart;
- VectorNormalize(V);
- SinCos(Q.RealPart / 2, SinA, CosA);
- A := V[X] * SinA;
- B := V[Y] * SinA;
- C := V[Z] * SinA;
-
- Result := IdentityMatrix;
- Result[X, X] := 1 - 2 * B * B - 2 * C * C;
- Result[X, Y] := 2 * A * B - 2 * CosA * C;
- Result[X, Z] := 2 * A * C + 2 * CosA * B;
-
- Result[Y, X] := 2 * A * B + 2 * CosA * C;
- Result[Y, Y] := 1 - 2 * A * A - 2 * C * C;
- Result[Y, Z] := 2 * B * C - 2 * CosA * A;
-
- Result[Z, X] := 2 * A * C - 2 * CosA * B;
- Result[Z, Y] := 2 * B * C + 2 * CosA * A;
- Result[Z, Z] := 1 - 2 * A * A - 2 * B * B;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure QuaternionToPoints(const Q: TQuaternion; var ArcFrom, ArcTo: TAffineVector); register;
-
- // converts a unit quaternion into two points on a unit sphere
-
- var S: Single;
-
- begin
- S := Sqrt(Q.ImagPart[X] * Q.ImagPart[X] + Q.ImagPart[Y] * Q.ImagPart[Y]);
- if S = 0 then ArcFrom := MakeAffineVector([0, 1, 0])
- else ArcFrom := MakeAffineVector([-Q.ImagPart[Y] / S, Q.ImagPart[X] / S, 0]);
- ArcTo[X] := Q.RealPart * ArcFrom[X] - Q.ImagPart[Z] * ArcFrom[Y];
- ArcTo[Y] := Q.RealPart * ArcFrom[Y] + Q.ImagPart[Z] * ArcFrom[X];
- ArcTo[Z] := Q.ImagPart[X] * ArcFrom[Y] - Q.ImagPart[Y] * ArcFrom[X];
- if Q.RealPart < 0 then ArcFrom := MakeAffineVector([-ArcFrom[X], -ArcFrom[Y], 0]);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MatrixAffineDeterminant(const M: TAffineMatrix): Single; register;
-
- // determinant of a 3x3 matrix
-
- begin
- Result := M[X, X] * (M[Y, Y] * M[Z, Z] - M[Z, Y] * M[Y, Z]) -
- M[X, Y] * (M[Y, X] * M[Z, Z] - M[Z, X] * M[Y, Z]) +
- M[X, Z] * (M[Y, X] * M[Z, Y] - M[Z, X] * M[Y, Y]);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MatrixDetInternal(a1, a2, a3, b1, b2, b3, c1, c2, c3: Single): Single;
-
- // internal version for the determinant of a 3x3 matrix
-
- begin
- Result := a1 * (b2 * c3 - b3 * c2) -
- b1 * (a2 * c3 - a3 * c2) +
- c1 * (a2 * b3 - a3 * b2);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure MatrixAdjoint(var M: TMatrix); register;
-
- // Adjoint of a 4x4 matrix - used in the computation of the inverse
- // of a 4x4 matrix
-
- var a1, a2, a3, a4,
- b1, b2, b3, b4,
- c1, c2, c3, c4,
- d1, d2, d3, d4: Single;
-
-
- begin
- a1 := M[X, X]; b1 := M[X, Y];
- c1 := M[X, Z]; d1 := M[X, W];
- a2 := M[Y, X]; b2 := M[Y, Y];
- c2 := M[Y, Z]; d2 := M[Y, W];
- a3 := M[Z, X]; b3 := M[Z, Y];
- c3 := M[Z, Z]; d3 := M[Z, W];
- a4 := M[W, X]; b4 := M[W, Y];
- c4 := M[W, Z]; d4 := M[W, W];
-
- // row column labeling reversed since we transpose rows & columns
- M[X, X] := MatrixDetInternal(b2, b3, b4, c2, c3, c4, d2, d3, d4);
- M[Y, X] := -MatrixDetInternal(a2, a3, a4, c2, c3, c4, d2, d3, d4);
- M[Z, X] := MatrixDetInternal(a2, a3, a4, b2, b3, b4, d2, d3, d4);
- M[W, X] := -MatrixDetInternal(a2, a3, a4, b2, b3, b4, c2, c3, c4);
-
- M[X, Y] := -MatrixDetInternal(b1, b3, b4, c1, c3, c4, d1, d3, d4);
- M[Y, Y] := MatrixDetInternal(a1, a3, a4, c1, c3, c4, d1, d3, d4);
- M[Z, Y] := -MatrixDetInternal(a1, a3, a4, b1, b3, b4, d1, d3, d4);
- M[W, Y] := MatrixDetInternal(a1, a3, a4, b1, b3, b4, c1, c3, c4);
-
- M[X, Z] := MatrixDetInternal(b1, b2, b4, c1, c2, c4, d1, d2, d4);
- M[Y, Z] := -MatrixDetInternal(a1, a2, a4, c1, c2, c4, d1, d2, d4);
- M[Z, Z] := MatrixDetInternal(a1, a2, a4, b1, b2, b4, d1, d2, d4);
- M[W, Z] := -MatrixDetInternal(a1, a2, a4, b1, b2, b4, c1, c2, c4);
-
- M[X, W] := -MatrixDetInternal(b1, b2, b3, c1, c2, c3, d1, d2, d3);
- M[Y, W] := MatrixDetInternal(a1, a2, a3, c1, c2, c3, d1, d2, d3);
- M[Z, W] := -MatrixDetInternal(a1, a2, a3, b1, b2, b3, d1, d2, d3);
- M[W, W] := MatrixDetInternal(a1, a2, a3, b1, b2, b3, c1, c2, c3);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MatrixDeterminant(const M: TMatrix): Single; register;
-
- // Determinant of a 4x4 matrix
-
- var a1, a2, a3, a4,
- b1, b2, b3, b4,
- c1, c2, c3, c4,
- d1, d2, d3, d4 : Single;
-
- begin
- a1 := M[X, X]; b1 := M[X, Y]; c1 := M[X, Z]; d1 := M[X, W];
- a2 := M[Y, X]; b2 := M[Y, Y]; c2 := M[Y, Z]; d2 := M[Y, W];
- a3 := M[Z, X]; b3 := M[Z, Y]; c3 := M[Z, Z]; d3 := M[Z, W];
- a4 := M[W, X]; b4 := M[W, Y]; c4 := M[W, Z]; d4 := M[W, W];
-
- Result := a1 * MatrixDetInternal(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
- b1 * MatrixDetInternal(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
- c1 * MatrixDetInternal(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
- d1 * MatrixDetInternal(a2, a3, a4, b2, b3, b4, c2, c3, c4);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure MatrixScale(var M: TMatrix; Factor: Single); register;
-
- // multiplies all elements of a 4x4 matrix with a factor
-
- var I, J: Integer;
-
- begin
- for I := 0 to 3 do
- for J := 0 to 3 do M[I, J] := M[I, J] * Factor;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure MatrixInvert(var M: TMatrix); register;
-
- // finds the inverse of a 4x4 matrix
-
- var Det: Single;
-
- begin
- Det := MatrixDeterminant(M);
- if Abs(Det) < EPSILON then M := IdentityMatrix
- else
- begin
- MatrixAdjoint(M);
- MatrixScale(M, 1 / Det);
- end;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure MatrixTranspose(var M: TMatrix); register;
-
- // computes transpose of 4x4 matrix
-
- var I, J: Integer;
- TM: TMatrix;
-
- begin
- for I := 0 to 3 do
- for J := 0 to 3 do TM[J, I] := M[I, J];
- M := TM;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- procedure MatrixAffineTranspose(var M: TAffineMatrix); register;
-
- // computes transpose of 3x3 matrix
-
- var I, J: Integer;
- TM: TAffineMatrix;
-
- begin
- for I := 0 to 2 do
- for J := 0 to 2 do TM[J, I] := M[I, J];
- M := TM;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MatrixMultiply(const M1, M2: TMatrix): TMatrix; register;
-
- // multiplies two 4x4 matrices
-
- var I, J: Integer;
- TM: TMatrix;
-
- begin
- for I := 0 to 3 do
- for J := 0 to 3 do
- TM[I, J] := M1[I, X] * M2[X, J] +
- M1[I, Y] * M2[Y, J] +
- M1[I, Z] * M2[Z, J] +
- M1[I, W] * M2[W, J];
- Result := TM;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function CreateRotationMatrix(const Axis: TVector3f; Angle: Single): TMatrix; register;
-
- // Creates a rotation matrix along the given Axis by the given Angle in radians.
-
- var cosine,
- sine,
- Len,
- one_minus_cosine: Extended;
-
- begin
- SinCos(Angle, Sine, Cosine);
- one_minus_cosine := 1 - cosine;
- Len := VectorNormalize(Axis);
-
- if Len = 0 then Result := IdentityMatrix
- else
- begin
- Result[X, X] := (one_minus_cosine * Sqr(Axis[0])) + Cosine;
- Result[X, Y] := (one_minus_cosine * Axis[0] * Axis[1]) - (Axis[2] * Sine);
- Result[X, Z] := (one_minus_cosine * Axis[2] * Axis[0]) + (Axis[1] * Sine);
- Result[X, W] := 0;
-
- Result[Y, X] := (one_minus_cosine * Axis[0] * Axis[1]) + (Axis[2] * Sine);
- Result[Y, Y] := (one_minus_cosine * Sqr(Axis[1])) + Cosine;
- Result[Y, Z] := (one_minus_cosine * Axis[1] * Axis[2]) - (Axis[0] * Sine);
- Result[Y, W] := 0;
-
- Result[Z, X] := (one_minus_cosine * Axis[2] * Axis[0]) - (Axis[1] * Sine);
- Result[Z, Y] := (one_minus_cosine * Axis[1] * Axis[2]) + (Axis[0] * Sine);
- Result[Z, Z] := (one_minus_cosine * Sqr(Axis[2])) + Cosine;
- Result[Z, W] := 0;
-
- Result[W, X] := 0;
- Result[W, Y] := 0;
- Result[W, Z] := 0;
- Result[W, W] := 1;
- end;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function ConvertRotation(const Angles: TAffineVector): TVector; register;
-
- { Turn a triplet of rotations about x, y, and z (in that order) into an
- equivalent rotation around a single axis (all in radians).
-
- Rotation of the Angle t about the axis (X, Y, Z) is given by:
-
- | X^2 + (1-X^2) Cos(t), XY(1-Cos(t)) + Z Sin(t), XZ(1-Cos(t))-Y Sin(t) |
- M = | XY(1-Cos(t))-Z Sin(t), Y^2 + (1-Y^2) Cos(t), YZ(1-Cos(t)) + X Sin(t) |
- | XZ(1-Cos(t)) + Y Sin(t), YZ(1-Cos(t))-X Sin(t), Z^2 + (1-Z^2) Cos(t) |
-
- Rotation about the three axes (Angles a1, a2, a3) can be represented as
- the product of the individual rotation matrices:
-
- | 1 0 0 | | Cos(a2) 0 -Sin(a2) | | Cos(a3) Sin(a3) 0 |
- | 0 Cos(a1) Sin(a1) | * | 0 1 0 | * | -Sin(a3) Cos(a3) 0 |
- | 0 -Sin(a1) Cos(a1) | | Sin(a2) 0 Cos(a2) | | 0 0 1 |
- Mx My Mz
-
- We now want to solve for X, Y, Z, and t given 9 equations in 4 unknowns.
- Using the diagonal elements of the two matrices, we get:
-
- X^2 + (1-X^2) Cos(t) = M[0][0]
- Y^2 + (1-Y^2) Cos(t) = M[1][1]
- Z^2 + (1-Z^2) Cos(t) = M[2][2]
-
- Adding the three equations, we get:
-
- X^2 + Y^2 + Z^2 - (M[0][0] + M[1][1] + M[2][2]) =
- - (3 - X^2 - Y^2 - Z^2) Cos(t)
-
- Since (X^2 + Y^2 + Z^2) = 1, we can rewrite as:
-
- Cos(t) = (1 - (M[0][0] + M[1][1] + M[2][2])) / 2
-
- Solving for t, we get:
-
- t = Acos(((M[0][0] + M[1][1] + M[2][2]) - 1) / 2)
-
- We can substitute t into the equations for X^2, Y^2, and Z^2 above
- to get the values for X, Y, and Z. To find the proper signs we note
- that:
-
- 2 X Sin(t) = M[1][2] - M[2][1]
- 2 Y Sin(t) = M[2][0] - M[0][2]
- 2 Z Sin(t) = M[0][1] - M[1][0]
- }
-
- var Axis1, Axis2: TVector3f;
- M, M1, M2: TMatrix;
- cost, cost1,
- sint,
- s1, s2, s3: Single;
- I: Integer;
-
-
- begin
- // see if we are only rotating about a single Axis
- if Abs(Angles[X]) < EPSILON then begin
- if Abs(Angles[Y]) < EPSILON then begin
- Result := MakeVector([0, 0, 1, Angles[Z]]);
- Exit;
- end else if Abs(Angles[Z]) < EPSILON then begin
- Result := MakeVector([0, 1, 0, Angles[Y]]);
- Exit;
- end
- end else if (Abs(Angles[Y]) < EPSILON) and (Abs(Angles[Z]) < EPSILON) then begin
- Result := MakeVector([1, 0, 0, Angles[X]]);
- Exit;
- end;
-
- // make the rotation matrix
- Axis1 := XVector;
- M := CreateRotationMatrix(Axis1, Angles[X]);
-
- Axis2 := YVector;
- M2 := CreateRotationMatrix(Axis2, Angles[Y]);
- M1 := MatrixMultiply(M, M2);
-
- Axis2 := ZVector;
- M2 := CreateRotationMatrix(Axis2, Angles[Z]);
- M := MatrixMultiply(M1, M2);
-
- cost := ((M[X, X] + M[Y, Y] + M[Z, Z])-1) / 2;
- if cost < -1 then
- cost := -1
- else if cost > 1 - EPSILON then begin
- // Bad Angle - this would cause a crash
- Result := MakeVector([1, 0, 0, 0]);
- Exit;
- end;
-
- cost1 := 1 - cost;
- Result := Makevector([Sqrt((M[X, X]-cost) / cost1),
- Sqrt((M[Y, Y]-cost) / cost1),
- sqrt((M[Z, Z]-cost) / cost1),
- arccos(cost)]);
-
- sint := 2 * Sqrt(1 - cost * cost); // This is actually 2 Sin(t)
-
- // Determine the proper signs
- for I := 0 to 7 do
- begin
- if (I and 1) > 1 then s1 := -1 else s1 := 1;
- if (I and 2) > 1 then s2 := -1 else s2 := 1;
- if (I and 4) > 1 then s3 := -1 else s3 := 1;
- if (Abs(s1 * Result[X] * sint-M[Y, Z] + M[Z, Y]) < EPSILON2) and
- (Abs(s2 * Result[Y] * sint-M[Z, X] + M[X, Z]) < EPSILON2) and
- (Abs(s3 * Result[Z] * sint-M[X, Y] + M[Y, X]) < EPSILON2) then
- begin
- // We found the right combination of signs
- Result[X] := Result[X] * s1;
- Result[Y] := Result[Y] * s2;
- Result[Z] := Result[Z] * s3;
- Exit;
- end;
- end;
- end;
-
- function CreateRotationMatrixX(Sine, Cosine: Single): TMatrix; register;
- begin
- Result := EmptyMatrix;
- Result[X, X] := 1;
- Result[Y, Y] := Cosine;
- Result[Y, Z] := Sine;
- Result[Z, Y] := -Sine;
- Result[Z, Z] := Cosine;
- Result[W, W] := 1;
- end;
-
- function CreateRotationMatrixY(Sine, Cosine: Single): TMatrix; register;
- begin
- Result := EmptyMatrix;
- Result[X, X] := Cosine;
- Result[X, Z] := -Sine;
- Result[Y, Y] := 1;
- Result[Z, X] := Sine;
- Result[Z, Z] := Cosine;
- Result[W, W] := 1;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function CreateRotationMatrixZ(Sine, Cosine: Single): TMatrix; register;
-
- // creates matrix for rotation about z-axis
-
- begin
- Result := EmptyMatrix;
- Result[X, X] := Cosine;
- Result[X, Y] := Sine;
- Result[Y, X] := -Sine;
- Result[Y, Y] := Cosine;
- Result[Z, Z] := 1;
- Result[W, W] := 1;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function CreateScaleMatrix(const V: TAffineVector): TMatrix; register;
-
- // creates scaling matrix
-
- begin
- Result := IdentityMatrix;
- Result[X, X] := V[X];
- Result[Y, Y] := V[Y];
- Result[Z, Z] := V[Z];
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function CreateTranslationMatrix(const V: TVector): TMatrix; register;
-
- // creates translation matrix
-
- begin
- Result := IdentityMatrix;
- Result[W, X] := V[X];
- Result[W, Y] := V[Y];
- Result[W, Z] := V[Z];
- Result[W, W] := V[W];
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function Lerp(Start, Stop, t: Single): Single;
-
- // calculates linear interpolation between start and stop at point t
-
- begin
- Result := Start + (Stop - Start) * t;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAffineLerp(const V1, V2: TAffineVector; t: Single): TAffineVector;
-
- // calculates linear interpolation between vector1 and vector2 at point t
-
- begin
- Result[X] := Lerp(V1[X], V2[X], t);
- Result[Y] := Lerp(V1[Y], V2[Y], t);
- Result[Z] := Lerp(V1[Z], V2[Z], t);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorLerp(const V1, V2: TVector; t: Single): TVector;
-
- // calculates linear interpolation between vector1 and vector2 at point t
-
- begin
- Result[X] := Lerp(V1[X], V2[X], t);
- Result[Y] := Lerp(V1[Y], V2[Y], t);
- Result[Z] := Lerp(V1[Z], V2[Z], t);
- Result[W] := Lerp(V1[W], V2[W], t);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function QuaternionSlerp(const QStart, QEnd: TQuaternion; Spin: Integer; t: Single): TQuaternion;
-
- // spherical linear interpolation of unit quaternions with spins
- // QStart, QEnd - start and end unit quaternions
- // t - interpolation parameter (0 to 1)
- // Spin - number of extra spin rotations to involve
-
- var beta, // complementary interp parameter
- theta, // Angle between A and B
- sint, cost, // sine, cosine of theta
- phi: Single; // theta plus spins
- bflip: Boolean; // use negativ t?
-
-
- begin
- // cosine theta
- cost := VectorAngle(QStart.ImagPart, QEnd.ImagPart);
-
- // if QEnd is on opposite hemisphere from QStart, use -QEnd instead
- if cost < 0 then
- begin
- cost := -cost;
- bflip := True;
- end
- else bflip := False;
-
- // if QEnd is (within precision limits) the same as QStart,
- // just linear interpolate between QStart and QEnd.
- // Can't do spins, since we don't know what direction to spin.
-
- if (1 - cost) < EPSILON then beta := 1 - t
- else
- begin
- // normal case
- theta := arccos(cost);
- phi := theta + Spin * Pi;
- sint := sin(theta);
- beta := sin(theta - t * phi) / sint;
- t := sin(t * phi) / sint;
- end;
-
- if bflip then t := -t;
-
- // interpolate
- Result.ImagPart[X] := beta * QStart.ImagPart[X] + t * QEnd.ImagPart[X];
- Result.ImagPart[Y] := beta * QStart.ImagPart[Y] + t * QEnd.ImagPart[Y];
- Result.ImagPart[Z] := beta * QStart.ImagPart[Z] + t * QEnd.ImagPart[Z];
- Result.RealPart := beta * QStart.RealPart + t * QEnd.RealPart;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- // VectorAffineCombine
- //
- function VectorAffineCombine(const V1, V2: TAffineVector; F1, F2: Single): TAffineVector;
- begin
- Result[X] := (F1 * V1[X]) + (F2 * V2[X]);
- Result[Y] := (F1 * V1[Y]) + (F2 * V2[Y]);
- Result[Z] := (F1 * V1[Z]) + (F2 * V2[Z]);
- end;
-
- // VectorAffineCombine3
- //
- function VectorAffineCombine3(const V1, V2, V3: TAffineVector; F1, F2, F3: Single): TAffineVector;
- begin
- Result[X] := (F1 * V1[X]) + (F2 * V2[X]) + (F3 * V3[X]);
- Result[Y] := (F1 * V1[Y]) + (F2 * V2[Y]) + (F3 * V3[Y]);
- Result[Z] := (F1 * V1[Z]) + (F2 * V2[Z]) + (F3 * V3[Z]);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorCombine(const V1, V2: TVector; F1, F2: Single): TVector;
-
- // makes a linear combination of two vectors and return the result
-
- begin
- Result[X] := (F1 * V1[X]) + (F2 * V2[X]);
- Result[Y] := (F1 * V1[Y]) + (F2 * V2[Y]);
- Result[Z] := (F1 * V1[Z]) + (F2 * V2[Z]);
- Result[W] := (F1 * V1[W]) + (F2 * V2[W]);
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function MatrixDecompose(const M: TMatrix; var Tran: TTransformations): Boolean; register;
-
- // Author: Spencer W. Thomas, University of Michigan
- //
- // MatrixDecompose - Decompose a non-degenerated 4x4 transformation matrix into
- // the sequence of transformations that produced it.
- //
- // The coefficient of each transformation is returned in the corresponding
- // element of the vector Tran.
- //
- // Returns true upon success, false if the matrix is singular.
-
- var I, J: Integer;
- LocMat,
- pmat,
- invpmat,
- tinvpmat: TMatrix;
- prhs,
- psol: TVector;
- Row: array[0..2] of TAffineVector;
-
- begin
- Result := False;
- locmat := M;
- // normalize the matrix
- if locmat[W, W] = 0 then Exit;
- for I := 0 to 3 do
- for J := 0 to 3 do
- locmat[I, J] := locmat[I, J] / locmat[W, W];
-
- // pmat is used to solve for perspective, but it also provides
- // an easy way to test for singularity of the upper 3x3 component.
-
- pmat := locmat;
- for I := 0 to 2 do pmat[I, W] := 0;
- pmat[W, W] := 1;
-
- if MatrixDeterminant(pmat) = 0 then Exit;
-
- // First, isolate perspective. This is the messiest.
- if (locmat[X, W] <> 0) or
- (locmat[Y, W] <> 0) or
- (locmat[Z, W] <> 0) then
- begin
- // prhs is the right hand side of the equation.
- prhs[X] := locmat[X, W];
- prhs[Y] := locmat[Y, W];
- prhs[Z] := locmat[Z, W];
- prhs[W] := locmat[W, W];
-
- // Solve the equation by inverting pmat and multiplying
- // prhs by the inverse. (This is the easiest way, not
- // necessarily the best.)
-
- invpmat := pmat;
- MatrixInvert(invpmat);
- MatrixTranspose(invpmat);
- psol := VectorTransform(prhs, tinvpmat);
-
- // stuff the answer away
- Tran[ttPerspectiveX] := psol[X];
- Tran[ttPerspectiveY] := psol[Y];
- Tran[ttPerspectiveZ] := psol[Z];
- Tran[ttPerspectiveW] := psol[W];
-
- // clear the perspective partition
- locmat[X, W] := 0;
- locmat[Y, W] := 0;
- locmat[Z, W] := 0;
- locmat[W, W] := 1;
- end
- else
- begin
- // no perspective
- Tran[ttPerspectiveX] := 0;
- Tran[ttPerspectiveY] := 0;
- Tran[ttPerspectiveZ] := 0;
- Tran[ttPerspectiveW] := 0;
- end;
-
- // next take care of translation (easy)
- for I := 0 to 2 do
- begin
- Tran[TTransType(Ord(ttTranslateX) + I)] := locmat[W, I];
- locmat[W, I] := 0;
- end;
-
- // now get scale and shear
- for I := 0 to 2 do
- begin
- row[I, X] := locmat[I, X];
- row[I, Y] := locmat[I, Y];
- row[I, Z] := locmat[I, Z];
- end;
-
- // compute X scale factor and normalize first row
- Tran[ttScaleX] := Sqr(VectorNormalize(row[0])); // ml: calculation optimized
-
- // compute XY shear factor and make 2nd row orthogonal to 1st
- Tran[ttShearXY] := VectorAffineDotProduct(row[0], row[1]);
- row[1] := VectorAffineCombine(row[1], row[0], 1, -Tran[ttShearXY]);
-
- // now, compute Y scale and normalize 2nd row
- Tran[ttScaleY] := Sqr(VectorNormalize(row[1])); // ml: calculation optimized
- Tran[ttShearXY] := Tran[ttShearXY]/Tran[ttScaleY];
-
- // compute XZ and YZ shears, orthogonalize 3rd row
- Tran[ttShearXZ] := VectorAffineDotProduct(row[0], row[2]);
- row[2] := VectorAffineCombine(row[2], row[0], 1, -Tran[ttShearXZ]);
- Tran[ttShearYZ] := VectorAffineDotProduct(row[1], row[2]);
- row[2] := VectorAffineCombine(row[2], row[1], 1, -Tran[ttShearYZ]);
-
- // next, get Z scale and normalize 3rd row
- Tran[ttScaleZ] := Sqr(VectorNormalize(row[1])); // (ML) calc. optimized
- Tran[ttShearXZ] := Tran[ttShearXZ] / tran[ttScaleZ];
- Tran[ttShearYZ] := Tran[ttShearYZ] / Tran[ttScaleZ];
-
- // At this point, the matrix (in rows[]) is orthonormal.
- // Check for a coordinate system flip. If the determinant
- // is -1, then negate the matrix and the scaling factors.
- if VectorAffineDotProduct(row[0], VectorCrossProduct(row[1], row[2])) < 0 then
- for I := 0 to 2 do
- begin
- Tran[TTransType(Ord(ttScaleX) + I)] := -Tran[TTransType(Ord(ttScaleX) + I)];
- row[I, X] := -row[I, X];
- row[I, Y] := -row[I, Y];
- row[I, Z] := -row[I, Z];
- end;
-
- // now, get the rotations out, as described in the gem
- Tran[ttRotateY] := arcsin(-row[0, Z]);
- if cos(Tran[ttRotateY]) <> 0 then
- begin
- Tran[ttRotateX] := arctan2(row[1, Z], row[2, Z]);
- Tran[ttRotateZ] := arctan2(row[0, Y], row[0, X]);
- end
- else
- begin
- tran[ttRotateX] := arctan2(row[1, X], row[1, Y]);
- tran[ttRotateZ] := 0;
- end;
- // All done!
- Result := True;
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorDblToFlt(const V: THomogeneousDblVector): THomogeneousVector; assembler;
-
- // converts a vector containing double sized values into a vector with single sized values
-
- asm
- FLD QWORD PTR [EAX]
- FSTP DWORD PTR [EDX]
- FLD QWORD PTR [EAX + 8]
- FSTP DWORD PTR [EDX + 4]
- FLD QWORD PTR [EAX + 16]
- FSTP DWORD PTR [EDX + 8]
- FLD QWORD PTR [EAX + 24]
- FSTP DWORD PTR [EDX + 12]
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAffineDblToFlt(const V: TAffineDblVector): TAffineVector; assembler;
-
- // converts a vector containing double sized values into a vector with single sized values
-
- asm
- FLD QWORD PTR [EAX]
- FSTP DWORD PTR [EDX]
- FLD QWORD PTR [EAX + 8]
- FSTP DWORD PTR [EDX + 4]
- FLD QWORD PTR [EAX + 16]
- FSTP DWORD PTR [EDX + 8]
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorAffineFltToDbl(const V: TAffineVector): TAffineDblVector; assembler;
-
- // converts a vector containing single sized values into a vector with double sized values
-
- asm
- FLD DWORD PTR [EAX]
- FSTP QWORD PTR [EDX]
- FLD DWORD PTR [EAX + 8]
- FSTP QWORD PTR [EDX + 4]
- FLD DWORD PTR [EAX + 16]
- FSTP QWORD PTR [EDX + 8]
- end;
-
- //----------------------------------------------------------------------------------------------------------------------
-
- function VectorFltToDbl(const V: TVector): THomogeneousDblVector; assembler;
-
- // converts a vector containing single sized values into a vector with double sized values
-
- asm
- FLD DWORD PTR [EAX]
- FSTP QWORD PTR [EDX]
- FLD DWORD PTR [EAX + 8]
- FSTP QWORD PTR [EDX + 4]
- FLD DWORD PTR [EAX + 16]
- FSTP QWORD PTR [EDX + 8]
- FLD DWORD PTR [EAX + 24]
- FSTP QWORD PTR [EDX + 12]
- end;
-
- //----------------- coordinate system manipulation functions -----------------------------------------------------------
-
- // Turn (Y axis)
- //
- function Turn(const Matrix: TMatrix; Angle: Single): TMatrix;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MakeAffineVector(Matrix[1]), Angle));
- end;
-
- // Turn (direction)
- //
- function Turn(const Matrix: TMatrix; const MasterUp: TAffineVector; Angle: Single): TMatrix;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MasterUp, Angle));
- end;
-
- // Pitch (X axis)
- //
- function Pitch(const Matrix: TMatrix; Angle: Single): TMatrix;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MakeAffineVector(Matrix[0]), Angle));
- end;
-
- // Pitch (direction)
- //
- function Pitch(const Matrix: TMatrix; const MasterRight: TAffineVector; Angle: Single): TMatrix; overload;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MasterRight, Angle));
- end;
-
- // Roll (Z axis)
- //
- function Roll(const Matrix: TMatrix; Angle: Single): TMatrix;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MakeAffineVector(Matrix[2]), Angle));
- end;
-
- // Roll (direction)
- //
- function Roll(const Matrix: TMatrix; const MasterDirection: TAffineVector; Angle: Single): TMatrix; overload;
- begin
- Result := MatrixMultiply(Matrix, CreateRotationMatrix(MasterDirection, Angle));
- end;
-
- // MakeShadowMatrix
- //
- function MakeShadowMatrix(const planePoint, planeNormal, lightPos : TVector) : TMatrix;
- var
- planeNormal3, dot : Single;
- begin
- // Find the last coefficient by back substitutions
- planeNormal3:=-( planeNormal[0]*planePoint[0]
- +planeNormal[1]*planePoint[1]
- +planeNormal[2]*planePoint[2]);
- // Dot product of plane and light position
- dot:= planeNormal[0]*lightPos[0]
- +planeNormal[1]*lightPos[1]
- +planeNormal[2]*lightPos[2]
- +planeNormal3 *lightPos[3];
- // Now do the projection
- // First column
- Result[0][0] := dot - lightPos[0] * planeNormal[0];
- Result[1][0] := - lightPos[0] * planeNormal[1];
- Result[2][0] := - lightPos[0] * planeNormal[2];
- Result[3][0] := - lightPos[0] * planeNormal3;
- // Second column
- Result[0][1] := - lightPos[1] * planeNormal[0];
- Result[1][1] := dot - lightPos[1] * planeNormal[1];
- Result[2][1] := - lightPos[1] * planeNormal[2];
- Result[3][1] := - lightPos[1] * planeNormal3;
- // Third Column
- Result[0][2] := - lightPos[2] * planeNormal[0];
- Result[1][2] := - lightPos[2] * planeNormal[1];
- Result[2][2] := dot - lightPos[2] * planeNormal[2];
- Result[3][2] := - lightPos[2] * planeNormal3;
- // Fourth Column
- Result[0][3] := - lightPos[3] * planeNormal[0];
- Result[1][3] := - lightPos[3] * planeNormal[1];
- Result[2][3] := - lightPos[3] * planeNormal[2];
- Result[3][3] := dot - lightPos[3] * planeNormal3;
- end;
-
- end.
-
-
-